/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       shroud_removers.cpp

	$Header: /game/shroud_removers.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "shroud_removers.h"

#include "adventure_map.h"
#include "adventure_map_window.h"
#include "adventure_sounds.h"
#include "adv_object_type.h"
#include "adventure_frame.h"
#include "army.h"
#include "basic_dialog.h"
#include "bound_handler.h"
#include "footprint.h"
#include "format_string.h"
#include "material_names.h"
#include "music.h"
#include "object_registration.h"
#include "override_input_handler.h"
#include "player.h"
#include "random.h"
#include "replace_keywords.h"

float const k_ai_value_of_shroud_remover = 750.0f;

// -----------------------------------------------------------------
// class object registration
// -----------------------------------------------------------------
static t_object_registration<t_cartographer> k_cartographer_registration( k_adv_object_cartographer );
static t_object_registration<t_magi_hut>     k_magi_hut_registration( k_adv_object_hut_of_the_magi );
static t_object_registration<t_tower>        k_tower_registration( k_adv_object_tower );

// -----------------------------------------------------------------
// string constants
// -----------------------------------------------------------------
static t_external_string const k_text_first_visit( "first_visit.shroud_removers" );
static t_external_string const k_text_re_visit( "re_visit.shroud_removers" );
static t_external_string const k_text_first_visit_magi( "first_visit_magi.shroud_removers" );
static t_external_string const k_text_re_visit_magi( "re_visit_magi.shroud_removers" );

static int const k_tower_visibility_radius		= 24;
static int const k_magi_visibility_radius		= 9;
static int const k_cartographer_land_radius		= k_tower_visibility_radius;
static int const k_cartographer_water_radius	= k_cartographer_land_radius * 3 / 2;

// -----------------------------------------------------------------
// cartographer
// -----------------------------------------------------------------
t_shroud_remover::t_shroud_remover( t_stationary_adventure_object const& source ) 
         : t_stationary_adventure_object( source )
{
}

float t_shroud_remover::ai_value( t_adventure_ai const& ai, t_creature_array const& army, int move_cost ) const
{
	int team = army.get_owner()->get_team();
	if (m_visited[team])
		return 0.0f;
	else
		return k_ai_value_of_shroud_remover;
}

void t_shroud_remover::activate_trigger( t_army* army, t_adv_map_point const& point, 
		                          t_direction direction, t_adventure_frame* frame )
{
	t_player* owner = army->get_owner();
	if (owner == NULL)
		return;

	///////////////////////////////////////////////////////////////////
	// replay code

	army->get_map()->record_visiblity_event ( army, point, direction, this );

	///////////////////////////////////////////////////////////////////


	// set the visited flag
	m_visited.set(owner->get_team());
}

void	t_shroud_remover::do_logic( t_army* army, t_adv_map_point const& point, 
								           t_direction direction, t_adventure_frame* frame)
{

}

void t_shroud_remover::show_dialog( bool visited , t_adv_map_point const& point )
{

}
// -----------------------------------------------------------------
// t_shroud_remover
// -----------------------------------------------------------------
bool t_shroud_remover::read( std::streambuf&				stream, 
					  t_qualified_adv_object_type const&	type,
					  int									version )
{
	if( version > 0 )
		m_visited = get<t_visited_bits>( stream );
	return true;
}

// -----------------------------------------------------------------
// t_shroud_remover
// -----------------------------------------------------------------
bool t_shroud_remover::write( std::streambuf& stream ) const
{
	put<t_visited_bits>( stream, m_visited );
	return true;
}

// -----------------------------------------------------------------
// -----------------------------------------------------------------
// -----------------------------------------------------------------
// -----------------------------------------------------------------

// -----------------------------------------------------------------
// cartographer
// -----------------------------------------------------------------
t_cartographer::t_cartographer( t_stationary_adventure_object const& source ) 
         : t_shroud_remover( source )
{
}

// -----------------------------------------------------------------
// cartographer
// -----------------------------------------------------------------
void t_cartographer::activate_trigger( t_army* army, t_adv_map_point const& point, 
		                          t_direction direction, t_adventure_frame* frame )
{
	if( !army || !frame ) 
		return;
	
	bool visited = get_visited(army->get_owner()->get_team());
	
	do_logic(army,point,direction,frame);
	
	if (army->get_owner() && !army->get_owner()->is_computer())
		show_dialog(visited,point);
}

void	t_cartographer::do_logic( t_army* army, t_adv_map_point const& point, 
								   t_direction direction, t_adventure_frame* frame)
{
	if( !army || !frame ) 
		return;

	t_visibility_set changed_tile_points;
	t_adventure_map* const map = frame->get_map();

	bool visited = get_visited(army->get_owner()->get_team());

	t_shroud_remover::activate_trigger( army, point, direction, frame );

	t_player* owner = army->get_owner();

	if( map )
	{
		t_map_point_2d const &		size = get_footprint().get_size();

		if( owner )
		{
			// increase visibility for water terrain
			map->increase_visibility(	owner->get_team(), 
										point, 
										size, 
										k_cartographer_water_radius, 
										k_tile_not_visible,
										changed_tile_points,
										k_increase_visibility_water_only );

			// increase visibility for land terrain
			map->increase_visibility(	owner->get_team(), 
										point, 
										size, 
										k_cartographer_land_radius, 
										k_tile_not_visible,
										changed_tile_points,
										k_increase_visibility_land_only );

			// update
			map->on_visibility_changed( owner->get_team(), point.level, changed_tile_points );
		}
	}
}

void t_cartographer::show_dialog( bool visited , t_adv_map_point const& point )
{
	t_basic_dialog*          dialog;
	std::string              text;
	
	// initialize dialog
	dialog = new t_basic_dialog;
	if ( !visited )
		text = replace_keywords( k_text_first_visit, "%name", get_name() );
	else
		text = replace_keywords( k_text_re_visit, "%name", get_name() );
	dialog->set_text( text );
	dialog->add_ok_button();
	dialog->set_title( get_name() );
	dialog->open( get_dialog_bottom( point ), true, k_align_bottom );
	t_sound_cache music = get_music_playing();
	stop_music();
	get_dialog_sound( k_dialog_sound_tower )->play( get_sound_volume() );
	dialog->run_modal();
	play_music( music );
}


// ------------------------------------------------------------------------
// The magi hut moves the view of the player across the list of eyeballs
// ------------------------------------------------------------------------
static int const k_magi_view_duration_ms	= 3000;

// ------------------------------------------------------------------------
// Initialize the adventure object
// ------------------------------------------------------------------------
t_magi_hut::t_magi_hut( t_stationary_adventure_object const& source ) 
         : t_shroud_remover( source ), t_idle_processor( 1, k_magi_view_duration_ms )
{
	m_curr_eye_live			= false;
	m_original_view_level	= -1;
	m_view_team				= -1;
}

// ------------------------------------------------------------------------
// Handle activation -- reveal appropriate parts of the map
// ------------------------------------------------------------------------
void t_magi_hut::activate_trigger( t_army* army, t_adv_map_point const& point, 
		                           t_direction direction, t_adventure_frame* frame )
{
	if( !army || !frame ) 
		return;

	assert( !is_active() );	// Why are we being trigger while still running from a previous trigger?
	assert( !m_curr_eye_live );


	// store original view position
	m_original_view_pos		= frame->get_map_window()->get_view_pos();
	m_original_view_level	= frame->get_map_window()->get_view_level();

	// Intialize our iteration controls
	m_first_time	= true;

	if (army->get_owner()) 
		m_view_team	= army->get_owner()->get_team();
	else
		m_view_team = -1;

	// Short cut if we're AI
	if (!army->get_owner() || army->get_owner()->is_computer())
	{
		do_logic( army, point, direction, frame );
		return;
	}


	// Remember if we've already visited
	const bool visited = get_visited( army->get_owner()->get_team() );

	// Call the base class (will mark us visited)
	t_shroud_remover::activate_trigger( army, point, direction, frame );

	// Present the message
	show_dialog( visited, point );


	// override all the game's input
	// any mouse or key press will abort the eye movement sequence
	m_input_handler.reset( new t_override_input );
	t_handler cancel_handler = bound_handler( *this, &t_magi_hut::cancel );
	m_input_handler->set_input_handler( cancel_handler );

	// Put ourselves into the idler list and give ourselves a jump start
	resume_idle_processing();
	on_idle();
}

// ------------------------------------------------------------------------
// Used by replay turn to recreat effects without UI
// ------------------------------------------------------------------------
void t_magi_hut::do_logic( t_army* army, t_adv_map_point const& point, 
								   t_direction direction, t_adventure_frame* frame)
{
	t_shroud_remover::activate_trigger( army, point, direction, frame );

	while (visit_next_eye( k_tile_not_visible ));
}

// ------------------------------------------------------------------------
// Cancel the automatic tour of the revealed regions
// ------------------------------------------------------------------------
void t_magi_hut::cancel()
{
	assert( get_map_window() );	// Why don't we have a map window?

	// Finish the tour in "AI" mode so the terrain is properly revealed
	while (visit_next_eye( k_tile_not_visible ));

	// Restore normal input operation and view location
	m_input_handler.reset();
	get_map_window()->move_view( m_original_view_pos );
	get_map_window()->get_frame()->set_view_level( m_original_view_level );

	// Stop receiving idle callbacks
	suspend_idle_processing();
}

// ------------------------------------------------------------------------
// Advance the automatic tour of the revealed regions
// ------------------------------------------------------------------------
void t_magi_hut::on_idle()
{
	if (!visit_next_eye( k_tile_visible ))
		cancel();
}

// ------------------------------------------------------------------------
// Visit the next eye on our tour
// ------------------------------------------------------------------------
bool t_magi_hut::visit_next_eye( t_tile_visibility visibility )
{
	assert( get_map() );	// Why are we running without a map?

	// Don't bother if we don't have a team
	if (m_view_team < 0)
		return false;

	t_visibility_set	changed_tile_points;
	int					cleared_level = -1;

	if (m_first_time)
	{
		// It's our first time, so initialize things
		m_curr_eye		= get_map()->get_magi_eyes().begin();
		m_first_time	= false;
	}
	else if (m_curr_eye != get_map()->get_magi_eyes().end() )
	{
		// See if we need to deactivate a currently "live" eye
		if ( m_curr_eye_live )
		{
			m_curr_eye_live = false;
			
			// Remove visibility from the current eye
			get_map()->remove_visibility(
				m_view_team,
				m_curr_eye->get_position(),
				m_curr_eye->get_footprint().get_size(),
				k_magi_visibility_radius,
				changed_tile_points );
			
			get_map()->on_visibility_changed( m_view_team, m_curr_eye->get_position().level, changed_tile_points );
			changed_tile_points.clear();
		}

		// Move on to the next eye
		m_curr_eye++;
	}

	// find the next eye that matches the window color
	while( m_curr_eye != get_map()->get_magi_eyes().end() && m_curr_eye->get_major_subtype() != get_major_subtype() )
		m_curr_eye++;

	// Reveal the map arround the new eye location
	if( m_curr_eye != get_map()->get_magi_eyes().end() )
	{
		t_adv_map_point obj_point(  );

		get_map()->increase_visibility(	
			m_view_team, 
			m_curr_eye->get_position(), 
			m_curr_eye->get_footprint().get_size(), 
			k_magi_visibility_radius, 
			visibility,
			changed_tile_points,
			k_increase_visibility_both );
		
		get_map()->on_visibility_changed( m_view_team, m_curr_eye->get_position().level, changed_tile_points );

		if (visibility == k_tile_visible)
		{
			// set the current map position
			get_map_window()->center_view( m_curr_eye->get_position() );

			m_curr_eye_live = true;
		}

		return true;
	}
	else
	{
		return false;	// we are done!
	}
}

// ------------------------------------------------------------------------
// class to view magi eyes on adventure map
// ------------------------------------------------------------------------
void t_magi_hut::show_dialog( bool visited , t_adv_map_point const& point )
{
	t_basic_dialog*          dialog;
	std::string              text;
	
	// initialize dialog
	dialog = new t_basic_dialog;
	if ( !visited )
		text = k_text_first_visit_magi;
	else
		text = k_text_re_visit_magi;
	dialog->set_text( text );
	dialog->add_ok_button();
	dialog->set_title( get_name() );
	dialog->open( get_dialog_bottom( point ), true, k_align_bottom );
	t_sound_cache music = get_music_playing();
	stop_music();
	get_dialog_sound( k_dialog_sound_tower )->play( get_sound_volume() );
	dialog->run_modal();
	play_music( music );
}

// -----------------------------------------------------------------
// -----------------------------------------------------------------
// -----------------------------------------------------------------
// -----------------------------------------------------------------
// -----------------------------------------------------------------

// tower
// -----------------------------------------------------------------
t_tower::t_tower( t_stationary_adventure_object const& source ) 
         : t_shroud_remover( source )
{

}

// -----------------------------------------------------------------
// tower
// -----------------------------------------------------------------
void t_tower::activate_trigger( t_army* army, t_adv_map_point const& point, 
		                          t_direction direction, t_adventure_frame* frame )
{	
	if( !army || !frame ) 
		return;

	bool visited = get_visited(army->get_owner()->get_team());
	
	do_logic(army,point,direction,frame);
	
	if (army->get_owner() && !army->get_owner()->is_computer())
		show_dialog(visited,point);
}

void t_tower::do_logic( t_army* army, t_adv_map_point const& point, 
							      t_direction direction, t_adventure_frame* frame)
{	
	if( !army || !frame ) 
		return;

	t_visibility_set changed_tile_points;
	t_adventure_map* const map = frame->get_map();

	bool visited = get_visited(army->get_owner()->get_team());

	t_shroud_remover::activate_trigger( army, point, direction, frame );

	t_player* owner = army->get_owner();

	if( map )
	{
		t_map_point_2d const &		size = get_footprint().get_size();

		if( owner )
		{
			// increase visibility for land terrain
			map->increase_visibility(	owner->get_team(), 
										point, 
										size, 
										k_tower_visibility_radius, 
										k_tile_not_visible,
										changed_tile_points,
										k_increase_visibility_both );

			// update
			map->on_visibility_changed( owner->get_team(), point.level, changed_tile_points );
		}
	}
}

void t_tower::show_dialog( bool visited , t_adv_map_point const& point )
{
	t_basic_dialog*          dialog;
	std::string              text;
	
	// initialize dialog
	dialog = new t_basic_dialog;
	if ( !visited )
		text = replace_keywords( k_text_first_visit, "%name", get_name() );
	else
		text = replace_keywords( k_text_re_visit, "%name", get_name() );
	dialog->set_text( text );
	dialog->add_ok_button();
	dialog->set_title( get_name() );
	dialog->open( get_dialog_bottom( point ), true, k_align_bottom );
	t_sound_cache music = get_music_playing();
	stop_music();
	get_dialog_sound( k_dialog_sound_tower )->play( get_sound_volume() );
	dialog->run_modal();
	play_music( music );
}
